"""
生成 JsonWebToken
"""

import base64
import hashlib
import json

from my_typing import *

_CHARSET: str = "UTF-8"


def base64_encode(content: bool | dict | float | int | list | None | str | tuple) -> bytes:
    """
    base64 编码

    +-------------------+---------------+
    | Python            | JSON          |
    +===================+===============+
    | dict              | object        |
    +-------------------+---------------+
    | list, tuple       | array         |
    +-------------------+---------------+
    | str               | string        |
    +-------------------+---------------+
    | int, float        | number        |
    +-------------------+---------------+
    | True              | true          |
    +-------------------+---------------+
    | False             | false         |
    +-------------------+---------------+
    | None              | null          |
    +-------------------+---------------+

    Args:
        content: 待编码的内容

    Returns:
        编码后的 bytes 格式内容

    Examples:
        >>> base64_encode(
        ...     content={
        ...         "name": "Darker",
        ...     },
        ... )
        b"eyJuYW1lIjogIkRhcmtlciJ9"

        >>> base64_encode(
        ...     content={
        ...         "name": "Darker",
        ...         "gender": "male",
        ...     },
        ... )
        b"eyJuYW1lIjogIkRhcmtlciIsICJnZW5kZXIiOiAibWFsZSJ9"
    """

    return base64.b64encode(json.dumps(content).encode(_CHARSET))


def sha256_encryption(
    content: bytes,
    salt: str,
) -> bytes:
    """
    SHA256 加密（单向加密，不可逆）

    Args:
        content: 待加密的 bytes 格式内容
        salt: 盐

    Returns:
        加密后的 bytes 格式内容

    Examples:
        >>> sha256_encryption(
        ...     content=b"eyJuYW1lIjogIkRhcmtlciJ9",  # noqa
        ...     salt="DARKER",
        ... )
        b"839fccf47a82280e742870cde7d49a4df03ea48a80aac602d9db0733837ce70d"
    """

    signature = hashlib.sha256()
    signature.update(content)
    signature.update(salt.encode(_CHARSET))

    return signature.hexdigest().encode(_CHARSET)


def generate_jwt(
    header: DictStrStr,
    payload: dict[str, IntOrStr],
    salt: str,
) -> str:
    """
    生成自定义 JSONWebToken

    Args:
        header: 头部信息，一般用于声明类型与加密方式（可用 base64 反解）
        payload: 荷载信息，一般用于存放过期时间等敏感信息（可用 base64 反解）
        salt: 盐（用于签名的加密）

    Returns:
        bytes 格式的 Token（由 3 部分组成，中间以 . 隔开，前 2 部分可以反解，最后 1 部分无法反解）

    Examples:
        >>> generate_jwt(
        ...     header={
        ...         "type": "JWT",
        ...         "hash": "SHA256".
        ...     },
        ...     payload={
        ...         "user_code": "USER21122874521451",
        ...     },
        ...     salt="0sdt#87h90s4%506bj8",
        ... )
        "eyJ0eXBlIjogIkpXVCIsICJoYXNoIjogIlNIQTI1NiJ9.eyJ1c2VyX2NvZGUiOiAiVVNFUjIxMTIyODc0NTIxNDUxIn0=.9ada04ce656db452ad134182abac346b99e500fc0fb3c548a0c616f868d5caf8"
    """

    header_result: bytes = base64_encode(header)
    payload_result: bytes = base64_encode(payload)
    header_payload_result: bytes = header_result + b"." + payload_result

    signature_result: bytes = sha256_encryption(header_payload_result, salt)
    res: bytes = header_payload_result + b"." + signature_result

    return res.decode(_CHARSET)


def generate_token(
    user_code: str,
    token_max_age: int,
    secret_key: str,
) -> str:
    """
    生成 Token
        - 固定长度：166
        - 由 3 部分组成，用 `.` 分割开
            - 1. header
            - 2. payload
            - 3. signature

    Args:
        user_code: 用户编号
        token_max_age: 过期时间（天）
        secret_key: 秘钥

    Returns:
        Token 字符串

    Examples:
        >>> generate_token(
        ...     user_code="USER21122874521451",
        ...     token_max_age=7,
        ... )
        "eyJ0eXBlIjogIkpXVCIsICJoYXNoIjogIlNIQTI1NiJ9.eyJjb2RlIjogIlVTRVIyMTEyMjg3NDUyMTQ1MSIsICJkYXkiOiA3fQ==.567d6c786cdbb3dae2ed100cb40419a44771b3b408da27444eb415c372f489df"
    """

    res: str = generate_jwt(
        header={
            "type": "JWT",
            "hash": "SHA256",
        },
        payload={
            "code": user_code,
            "day": token_max_age,
        },
        salt=secret_key,
    )

    return res


def get_signature(
    headers: str,
    payloads: str,
    salt: str,
) -> str:
    """
    根据 header & payload & salt 获取解密后的 signature

    Args:
        headers: 头部信息
        payloads: 荷载信息
        salt: 盐

    Returns:
        解密后的 signature

    Examples:
        >>> get_signature(
        ...     headers="eyJ0eXBlIjogIkpXVCIsICJoYXNoIjogIlNIQTI1NiJ9",  # noqa
        ...     payloads="eyJjb2RlIjogIlVTRVIyMTEyMjg3NDUyMTQ1MSIsICJkYXkiOiA3fQ==",  # noqa
        ...     salt="0sdt#87h90s4%506bj8",
        ... )
        "b7a3ecb1f2431567c12aa14e66ef9eb973c9e8d0db9329df65859ae092782d7e"
    """

    header_payload_result: bytes = headers.encode(_CHARSET) + b"." + payloads.encode(_CHARSET)

    return sha256_encryption(header_payload_result, salt).decode(_CHARSET)
